package types

import (
	"bytes"
	"fmt"
	"gitee.com/sunliang711/locker-control/server/utils"
	"log"
)

//数据帧格式
//起始符(4B) 帧长度(1B) 板地址(1B:固定值0x00) 指令字(1B) 数据域(nB) 校验(1B)
//总长度8+n
//校验是从起始符开始到数据域最后字节逐字节异或值(XOR)

const (
	//数据帧格式始终使用FrameHeader开头
	FrameHeader = "NBSE"

	//板号,固定值0x00
	BoardNo = 0x00
)

type Cmd byte

//2g Cmds
const (
	CmdRegister2G Cmd = iota + 0x30
	CmdHeart2G
	CmdOpen2G
	CmdStatus2G
	CmdAllStatus2G
	CmdClientPushStatus2G
	CmdOpenAll2G
	CmdChangeId2G
	CmdProtocolHeader2G
)

//4g Cmds
const (
	CmdHeart4G Cmd = iota + 0x80
	CmdId4G
	CmdOpen4G
	CmdStatus4G
	CmdAllStatus4G
	CmdClientPushStatus4G
	CmdOpenAll4G

	CmdPasswd4G Cmd = iota + 0x91
	CmdIpPort4G
)

type Frame struct {
	Header   []byte `json:"header"`
	Len      byte   `json:"len"`
	BoardNo  byte   `json:"board_no"`
	Cmd      Cmd    `json:"cmd"`
	Data     []byte `json:"data"`
	CheckSum byte   `json:"check_sum"`
}

func (f *Frame) String() string {
	return fmt.Sprintf(`{Header: %v,Len: %v,BoardNo: %v,Cmd: %v,Data: %v,CheckSum: %v}`,
		utils.Bytes2String(f.Header), f.Len, f.BoardNo, f.Cmd, utils.Bytes2String(f.Data), f.CheckSum)
}

//1. New
func NewFrame(cmd Cmd, data []byte) *Frame {
	frame := &Frame{
		Header:   []byte(FrameHeader),
		Len:      0,
		BoardNo:  BoardNo,
		Cmd:      cmd,
		Data:     data,
		CheckSum: 0,
	}
	frame.Len = byte(len(frame.Header) + 1 + 1 + 1 + len(data) + 1)

	frame.CheckSum = utils.Xor(frame.pureData())
	return frame
}

func (frame *Frame) pureData() []byte {
	ret := make([]byte, frame.Len-1)
	index := 0
	copy(ret[index:], frame.Header)
	index += len(frame.Header)
	copy(ret[index:], []byte{frame.Len})
	index += 1
	copy(ret[index:], []byte{frame.BoardNo})
	index += 1
	copy(ret[index:], []byte{byte(frame.Cmd)})
	index += 1
	if frame.Data != nil {
		copy(ret[index:], frame.Data)
		index += len(frame.Data)
	}

	return ret
}

//2. Bytes
func (frame *Frame) Bytes() []byte {
	ret := make([]byte, frame.Len)
	copy(ret, frame.pureData())
	ret[len(ret)-1] = frame.CheckSum
	return ret
}

func NewFrameFromBytes(bs []byte) (*Frame, error) {
	if !utils.CheckFrameBytes(bs) {
		return nil, fmt.Errorf("Frame bytes less than 2 bytes or checksum not match")
	}
	//check header
	header := []byte(FrameHeader)
	if bytes.Compare(header, bs[:len(header)]) != 0 {
		msg := "header not match"
		log.Printf(msg)
		return nil, fmt.Errorf(msg)
	}
	index := 0

	frame := &Frame{}
	frame.Header = bs[index:len(FrameHeader)]
	index += len(FrameHeader)

	frame.Len = bs[index]
	index += 1

	frame.BoardNo = bs[index]
	index += 1

	frame.Cmd = Cmd(bs[index])
	index += 1

	frame.Data = bs[index : len(bs)-1]
	index += len(bs) - 1 - index

	frame.CheckSum = bs[index]

	return frame, nil
}
